home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
10,000 Great Games
/
10,000 Great Games.iso
/
Product
/
66
/
data1.cab
/
Source_Files
/
Src
/
Movable.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-01-16
|
9KB
|
432 lines
#include "stdafx.h"
cPush::cPush(cPush **list, fix duration, fix _vx, fix _vy, fix _ax, fix _ay)
{
vx = _vx;
vy = _vy;
ax = _ax;
ay = _ay;
timeleft = duration;
ptimer = 0;
add((cList **)list);
}
cMovable::cMovable(cProperties *_orig)
: cAudible(_orig)
{
pushes = 0;
stop_movement();
resting = FALSE;
horizontal_friction = orig->params->get_bool("*HORIZONTAL_FRICTION", TRUE);
bounce_loss = orig->params->get_int("*BOUNCE_LOSS", 3);
one_time_bounce = orig->params->get_bool("*ONE_TIME_BOUNCE", FALSE);
last_bounce_y = MAXINT;
}
cMovable::~cMovable()
{
pushes->delete_list();
}
int cMovable::control()
{
fix dx, dy, dv, dt;
// Do changing of picture
cAudible::control();
// Save old variables
ox = x, oy = y;
// Compute time difference
dt = vtimer.delta();
// Do movement in x direction
dv = ax * dt;
dx = (vx + (dv >> 1)) * dt;
vx += dv;
// Do movement in y direction
dv = ay * dt;
dy = (vy + (dv >> 1)) * dt;
vy += dv;
// Handle pushes
cPush *n;
int kill;
for (cPush *p = pushes; p != 0; p = n)
{
n = (cPush *)p->next;
// First get time difference
dt = p->ptimer.delta();
// Check if push is ending here
if (dt > p->timeleft)
{
dt = p->timeleft;
kill = TRUE;
}
else
{
p->timeleft -= dt;
kill = FALSE;
}
// Do movement in x direction
dv = p->ax * dt;
dx += (p->vx + (dv >> 1)) * dt;
p->vx += dv;
// Do movement in y direction
dv = p->ay * dt;
dy += (p->vy + (dv >> 1)) * dt;
p->vy += dv;
// Check if there's some time left
if (kill)
{
// Add aquired speed permanently
vx += p->vx, vy += p->vy;
// Remove this object
delete p;
}
}
// Set new position
if (dx != (fix)0 || dy != (fix)0)
set_position(fx + dx, fy + dy);
return TRUE;
}
int cMovable::check_resting_on_boundaries(cLine *obj, cDisplayable *bound)
{
// The function computes the boundary lines (for obj) before and after
// movement computed by control(). It forms a square with the new
// boundary line as base and the height of the previous boundary line
// as height.
//
// After that it searches for lines that fall in the area and picks out
// the first the object would encounter. The object is placed correctly
// interpolated on this line. This function only detects lines when the
// object is going down.
ASSERT(obj != 0);
// Get old (oy1) and new (y1, x1, x2) boundary line for object
int oy1 = oy - obj->y1, y1 = y - obj->y1;
int x1 = x + obj->x1, x2 = x + obj->x2;
sort2(x1, x2);
// Consider only the case: oy1 >= y1, we're going down
if (oy1 >= y1)
{
int y_max = MININT;
// Loop through displayables
for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
{
// Loop trough lines
for (cLine *l = d->line_bounds; l != 0; l = (cLine *)l->next)
{
int lx1 = d->x + l->x1, lx2 = d->x + l->x2, ly1 = d->y - l->y1;
sort2(lx1, lx2);
if (lx2 >= x1 && x2 >= lx1 && oy1 >= ly1 && ly1 >= y1)
y_max = ly1;
}
}
// If there was a boundary, set the object on this boundary
if (y_max > MININT)
{
// Put object on the edge
set_position(fx, (fix)(y_max + obj->y1));
// We're on boundary
return TRUE;
}
}
// We're not on boundary
return FALSE;
}
void cMovable::bounce_on_boundaries()
{
// This function is an extension from check_resting_on_boundaries,
// it handles the bouncing and resting of objects on platforms.
// Bounce on walls
if (!x_on_screen())
vx = -vx / bounce_loss;
// Check if we fell onto something
if (orig->line != 0
&& (!one_time_bounce || oy < last_bounce_y)
&& check_resting_on_boundaries(line_bounds, structures)
)
{
// Modify horizontal speed
if (horizontal_friction)
{
if (abs(vx) < (fix)10)
vx = 0;
else if (!is_x_pushed())
new_x_push(0.5, 0, -vx);
}
// Modify vertical speed, when velocity is smaller than 70 upwards
// (approx 10 pixels total movement) set status to resting
if ((vy >= (fix)0 && vy < (fix)70) || resting)
vy = 0, resting = TRUE;
else
vy = -vy / bounce_loss;
last_bounce_y = y;
}
else
{
resting = FALSE;
}
// Set vertical acceleration
ay = GRAVITY;
}
void cMovable::move_objects_on_boundaries(cLine *obj, cDisplayable *bound)
{
// The function computes the boundary lines (for obj) before and after
// movement computed by control(). It forms a square with the new
// boundary line as base and the height of the previous boundary line
// as height.
//
// After that it searches for lines that fall in the area and moves all
// objects by the amount this movable moved.
ASSERT(obj != 0);
// Get old and new (y1, y2, x1, x2) boundary lines for object
int y1 = oy - obj->y1, y2 = y - obj->y1;
sort2(y1, y2);
int x1 = x + obj->x1, x2 = x + obj->x2;
sort2(x1, x2);
// Loop through displayables
for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
{
// Loop trough lines
for (cLine *l = d->line_bounds; l != 0; l = (cLine *)l->next)
{
int lx1 = d->x + l->x1, lx2 = d->x + l->x2, ly1 = d->y - l->y1;
sort2(lx1, lx2);
if (lx2 >= x1 && x2 >= lx1 && y1 <= ly1 && ly1 <= y2)
{
d->make_dirty();
d->set_position(d->fx + (fix)(x - ox), fy - (fix)obj->y1 + (fix)l->y1);
d->make_dirty();
}
}
}
}
cDisplayable *cMovable::check_radial_boundaries(cCircle *obj, cDisplayable *bound, int (*callback)(cMovable *, cDisplayable *, cCircle *, cCircle *), cDisplayable *ignore, int reposition)
{
// This function determines if there is any object in "bound"
// that is in radius of circle(s) "obj" and calls callback (if
// not zero) for every object. It returns the closest object.
// Distance that object travelled in x and y direction
int dx = x - ox, dy = y - oy;
// Distance squared
int dd = d_square(dx, dy);
// Closest object, minimum distance found and position of that object
cDisplayable *closest = 0, *last_callback = 0;
int d_min = MAXINT, closest_x = 0, closest_y = 0;
// Loop through circles
for (cCircle *o = obj; o != 0; o = (cCircle *)o->next)
{
// Start position
int sx = ox + o->x, sy = oy - o->y;
// End position
int ex = x + o->x, ey = y - o->y;
// Loop through objects
for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
{
// Loop through circles for every object
if (d != this && d != ignore)
for (cCircle *c = d->circle_bounds; c != 0; c = (cCircle *)c->next)
{
// Locus of circle of object d, circle c
int cx = d->x + c->x, cy = d->y - c->y;
// Point of closest approach fraction: t/dd of whole vector
int t = (cx - sx) * dx + (cy - sy) * dy;
// If t < 0 or t > length of vector choose end points, else
// choose closest point
int qx, qy;
if (t <= 0)
qx = sx, qy = sy;
else if (t >= dd)
qx = ex, qy = ey;
else
qx = sx + t*dx/dd, qy = sy + t*dy/dd;
// Compute distance
int dist = d_square(cx - qx, cy - qy);
// Check if it is in range
if (dist < square(o->radius + c->radius))
{
if (callback != 0 && (last_callback == d || !callback(this, d, o, c)))
continue;
last_callback = d;
if (dist - c->radius < d_min)
{
d_min = dist - c->radius;
closest = d;
closest_x = qx;
closest_y = qy;
}
}
}
}
}
// Move to closest position
if (reposition && closest != 0)
set_position(closest_x, closest_y);
// Return closest object
return closest;
}
void cMovable::add_position(fix dx, fix dy)
{
set_position(fx + dx, fy + dy);
}
void cMovable::add_angular_position(fix d, fix angle)
{
set_position(fx + d * cos(angle), fy + d * sin(angle));
}
fix cMovable::get_speed(fix angle)
{
return vx * cos(angle) + vy * sin(angle);
}
void cMovable::set_angular_speed(fix v, fix angle)
{
vx = v * cos(angle);
vy = v * sin(angle);
}
void cMovable::add_angular_speed(fix v, fix angle)
{
vx += v * cos(angle);
vy += v * sin(angle);
}
fix cMovable::get_acceleration(fix angle)
{
return ax * cos(angle) + ay * sin(angle);
}
void cMovable::set_angular_acceleration(fix a, fix angle)
{
ax = a * cos(angle);
ay = a * sin(angle);
}
void cMovable::add_angular_acceleration(fix a, fix angle)
{
ax += a * cos(angle);
ay += a * sin(angle);
}
int cMovable::is_x_pushed()
{
for (cPush *p = pushes; p != 0; p = (cPush *)p->next)
if (p->vx != (fix)0 || p->ax != (fix)0)
return TRUE;
return FALSE;
}
int cMovable::is_y_pushed()
{
for (cPush *p = pushes; p != 0; p = (cPush *)p->next)
if (p->vy != (fix)0 || p->ay != (fix)0)
return TRUE;
return FALSE;
}